fix(pipeline): allow resuming a snapshot paused on a later loop visit - #12152
fix(pipeline): allow resuming a snapshot paused on a later loop visit#12152Anai-Guo wants to merge 1 commit into
Conversation
When a breakpoint fired on the second-or-later visit of a component inside a loop, resuming the snapshot immediately failed with PipelineComponentsBlockedError. The paused component's inputs are restored as user inputs (sender is not preserved through serialization), and user inputs only trigger a component on its first visit, so the resumed component was never selected to run. Flag the component the run resumes at as a resume target when filling the initial priority queue, so it can trigger exactly once regardless of its visit count. Fixes deepset-ai#12145
|
@Anai-Guo is attempting to deploy a commit to the deepset Team on Vercel. A member of the Team first needs to authorize it. |
|
|
|
Hi @Anai-Guo, thanks a lot for your contribution! 🙏 We noticed that the Contributor License Agreement (CLA) check ( To get your PR reviewed, please sign the CLA via the link in the |
|
Closing as fixing in #12162 |
Related Issues
Fixes #12145
Proposed Changes
Resuming a pipeline from a
pipeline_snapshotfailed withPipelineComponentsBlockedErrorwhen the breakpoint had been hit on the second (or later) visit of a component inside a loop. First-visit resume worked; later-visit resume did not.Root cause. When a snapshot is created, the paused component's inputs are stored and later restored through
_convert_to_internal_format, which marks every restored input withsender = None— i.e. they look like user inputs. Inhas_any_trigger, a user input can only trigger a component on its first visit (component["visits"] == 0). On a loop re-visit the restoredcomponent_visitsfor that component is already>= 1, so:trigger_from_predecessorisFalse(sender isNone),trigger_from_userisFalse(gated onvisits == 0),trigger_without_inputsisFalse(the component has senders),so the component the run is supposed to resume at is marked
BLOCKEDand never selected, and the pipeline reports itself blocked.Fix. The run already knows which component it is resuming at (
pipeline_snapshot.break_point.component_name). When filling the initial priority queue, that component is now flagged as a resume target so it can trigger exactly once regardless of its visit count. The flag is only set during the initial fill on the resume path; every other queue fill (normal runs, the async path, and mid-loop refills) is unchanged, so a resumed component continues to be driven by predecessor triggers on subsequent iterations.Changes:
has_any_triggergains atrigger_from_resumecondition (active only when the component carries theis_resumemarker and has restored inputs)._fill_queueaccepts an optionalresume_component_nameand tags that one component.Pipeline.runpasses the resume component name into the initial_fill_queuecall.How did you test it?
test_resume_snapshot_taken_on_a_later_loop_visit) reproducing the issue exactly (breakpoint onvisit_count=1inside aBranchJoinerloop), asserting the resume no longer raises and finishes the loop.visit_count=0), and pausing/resuming a non-greedy component on a later visit all behave correctly.test/core/pipeline/test_component_checks.pysuite (74 tests) still passes.Notes
Scope is limited to the reported
PipelineComponentsBlockedError. An orthogonal, pre-existing snapshot-resume limitation involving greedy joiners over multiple loop hops (reproducible onmainwithout this change) is intentionally left out of scope.This PR was written with AI assistance (Claude Code) and reviewed by me before submission.
🤖 Generated with Claude Code